package file

import (
	"archive/zip"
	"bytes"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"strings"
)

// ZipFile 自动将指定文件压缩为.zip文件
// 参数: filename - 要压缩的文件路径
// 返回: 压缩后的文件路径和可能的错误
func ZipFile(filename string) (string, error) {
	// 检查文件是否存在
	if _, err := os.Stat(filename); os.IsNotExist(err) {
		return "", fmt.Errorf("文件不存在: %s", filename)
	}

	// 创建输出文件
	zipFilename := filepath.Join(filepath.Dir(filename), filepath.Base(filename)+".zip")
	zipFile, err := os.Create(zipFilename)
	if err != nil {
		return "", fmt.Errorf("创建压缩文件失败: %v", err)
	}
	defer zipFile.Close()

	// 创建zip writer
	zipWriter := zip.NewWriter(zipFile)
	defer zipWriter.Close()

	// 打开要压缩的文件
	inputFile, err := os.Open(filename)
	if err != nil {
		return "", fmt.Errorf("打开文件失败: %v", err)
	}
	defer inputFile.Close()

	// 获取文件信息
	fileInfo, err := inputFile.Stat()
	if err != nil {
		return "", fmt.Errorf("获取文件信息失败: %v", err)
	}

	// 创建zip中的文件头
	header, err := zip.FileInfoHeader(fileInfo)
	if err != nil {
		return "", fmt.Errorf("创建文件头失败: %v", err)
	}
	header.Name = filepath.Base(filename)
	header.Method = zip.Deflate

	// 创建zip中的文件
	writer, err := zipWriter.CreateHeader(header)
	if err != nil {
		return "", fmt.Errorf("创建zip文件失败: %v", err)
	}

	// 复制文件内容到zip
	_, err = io.Copy(writer, inputFile)
	if err != nil {
		return "", fmt.Errorf("复制文件内容失败: %v", err)
	}

	return zipFilename, nil
}

// ZipDirectory 自动将指定目录压缩为.zip文件
// 参数: dirpath - 要压缩的目录路径
// 返回: 压缩后的文件路径和可能的错误
func ZipDirectory(dirpath string) (string, error) {
	// 检查目录是否存在
	if _, err := os.Stat(dirpath); os.IsNotExist(err) {
		return "", fmt.Errorf("目录不存在: %s", dirpath)
	}

	// 确保是目录
	fileInfo, err := os.Stat(dirpath)
	if err != nil {
		return "", fmt.Errorf("获取目录信息失败: %v", err)
	}
	if !fileInfo.IsDir() {
		return "", fmt.Errorf("指定路径不是目录: %s", dirpath)
	}

	// 创建输出文件
	zipFilename := dirpath + ".zip"
	zipFile, err := os.Create(zipFilename)
	if err != nil {
		return "", fmt.Errorf("创建压缩文件失败: %v", err)
	}
	defer zipFile.Close()

	// 创建zip writer
	zipWriter := zip.NewWriter(zipFile)
	defer zipWriter.Close()

	// 遍历目录中的所有文件
	err = filepath.Walk(dirpath, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		// 获取相对路径
		relPath, err := filepath.Rel(dirpath, path)
		if err != nil {
			return err
		}

		// 跳过根目录
		if relPath == "." {
			return nil
		}

		// 创建文件头
		header, err := zip.FileInfoHeader(info)
		if err != nil {
			return err
		}

		// 设置文件名（保持目录结构）
		header.Name = filepath.ToSlash(relPath)

		// 如果是目录，确保以/结尾
		if info.IsDir() {
			header.Name += "/"
		} else {
			header.Method = zip.Deflate
		}

		// 创建zip中的文件/目录
		writer, err := zipWriter.CreateHeader(header)
		if err != nil {
			return err
		}

		// 如果是文件，复制内容
		if !info.IsDir() {
			file, err := os.Open(path)
			if err != nil {
				return err
			}
			defer file.Close()

			_, err = io.Copy(writer, file)
			if err != nil {
				return err
			}
		}

		return nil
	})

	if err != nil {
		return "", fmt.Errorf("压缩目录失败: %v", err)
	}

	return zipFilename, nil
}

// ZipFileReader 自动将指定文件压缩为.zip文件并返回io.Reader
// 参数: filename - 要压缩的文件路径
// 返回: 压缩后的文件io.Reader和可能的错误
func ZipFileReader(filename string) (io.Reader, error) {
	// 检查文件是否存在
	if _, err := os.Stat(filename); os.IsNotExist(err) {
		return nil, fmt.Errorf("文件不存在: %s", filename)
	}

	// 创建内存中的zip文件
	zipBuffer := new(bytes.Buffer)
	zipWriter := zip.NewWriter(zipBuffer)

	// 打开要压缩的文件
	inputFile, err := os.Open(filename)
	if err != nil {
		return nil, fmt.Errorf("打开文件失败: %v", err)
	}

	// 获取文件信息
	fileInfo, err := inputFile.Stat()
	if err != nil {
		inputFile.Close()
		return nil, fmt.Errorf("获取文件信息失败: %v", err)
	}

	// 创建zip中的文件头
	header, err := zip.FileInfoHeader(fileInfo)
	if err != nil {
		inputFile.Close()
		return nil, fmt.Errorf("创建文件头失败: %v", err)
	}
	header.Name = filepath.Base(filename)
	header.Method = zip.Deflate

	// 创建zip中的文件
	writer, err := zipWriter.CreateHeader(header)
	if err != nil {
		inputFile.Close()
		return nil, fmt.Errorf("创建zip文件失败: %v", err)
	}

	// 使用Goroutine在后台进行压缩，以支持流式处理
	reader, writerPipe := io.Pipe()

	go func() {
		// 复制文件内容到zip
		_, err := io.Copy(writer, inputFile)
		if err != nil {
			inputFile.Close()
			zipWriter.Close()
			writerPipe.CloseWithError(fmt.Errorf("复制文件内容失败: %v", err))
			return
		}

		// 关闭文件和zip writer
		inputFile.Close()
		err = zipWriter.Close()
		if err != nil {
			writerPipe.CloseWithError(fmt.Errorf("关闭zip writer失败: %v", err))
			return
		}

		// 将压缩后的内容写入管道
		_, err = zipBuffer.WriteTo(writerPipe)
		if err != nil {
			writerPipe.CloseWithError(fmt.Errorf("写入压缩数据失败: %v", err))
			return
		}

		// 成功关闭管道
		writerPipe.Close()
	}()

	return reader, nil
}

// UnzipFile 解压.zip文件到指定目录
// 参数: zipFilename - 要解压的.zip文件路径
//      destDir - 解压目标目录
// 返回: 可能的错误
func UnzipFile(zipFilename, destDir string) error {
	// 检查zip文件是否存在
	if _, err := os.Stat(zipFilename); os.IsNotExist(err) {
		return fmt.Errorf("zip文件不存在: %s", zipFilename)
	}

	// 创建目标目录
	if err := os.MkdirAll(destDir, 0755); err != nil {
		return fmt.Errorf("创建目标目录失败: %v", err)
	}

	// 打开zip文件
	reader, err := zip.OpenReader(zipFilename)
	if err != nil {
		return fmt.Errorf("打开zip文件失败: %v", err)
	}
	defer reader.Close()

	// 遍历zip文件中的每个文件/目录
	for _, file := range reader.File {
		// 构造解压后的文件路径
		filePath := filepath.Join(destDir, file.Name)

		// 检查是否为目录
		if file.FileInfo().IsDir() {
			// 创建目录
			if err := os.MkdirAll(filePath, file.Mode()); err != nil {
				return fmt.Errorf("创建目录失败: %v", err)
			}
			continue
		}

		// 创建文件所在的目录
		if err := os.MkdirAll(filepath.Dir(filePath), 0755); err != nil {
			return fmt.Errorf("创建文件目录失败: %v", err)
		}

		// 打开zip中的文件
		zipFile, err := file.Open()
		if err != nil {
			return fmt.Errorf("打开zip中的文件失败: %v", err)
		}

		// 创建目标文件
		targetFile, err := os.OpenFile(filePath, os.O_CREATE|os.O_RDWR|os.O_TRUNC, file.Mode())
		if err != nil {
			zipFile.Close()
			return fmt.Errorf("创建目标文件失败: %v", err)
		}

		// 复制文件内容
		_, err = io.Copy(targetFile, zipFile)
		targetFile.Close()
		zipFile.Close()

		if err != nil {
			return fmt.Errorf("复制文件内容失败: %v", err)
		}
	}

	return nil
}

// UnzipDirectory 解压.zip文件并保持目录结构
// 参数: zipFilename - 要解压的.zip文件路径
// 返回: 解压后的目录路径和可能的错误
func UnzipDirectory(zipFilename string) (string, error) {
	// 检查zip文件是否存在
	if _, err := os.Stat(zipFilename); os.IsNotExist(err) {
		return "", fmt.Errorf("zip文件不存在: %s", zipFilename)
	}

	// 获取zip文件所在目录和文件名
	dir := filepath.Dir(zipFilename)
	baseName := filepath.Base(zipFilename)
	
	// 移除.zip扩展名作为解压目录名
	destDir := filepath.Join(dir, strings.TrimSuffix(baseName, filepath.Ext(baseName)))

	// 调用UnzipFile函数解压到指定目录
	if err := UnzipFile(zipFilename, destDir); err != nil {
		return "", err
	}

	return destDir, nil
}
